package com.ccl.iot;

import java.math.BigInteger;

public abstract class IOTDataType<TYPE> {
    IOTDataType() {
    }

    IOTDataType(TYPE aValue) {
        setValue(aValue);
    }

    public abstract TYPE getValue();

    public abstract void setValue(TYPE aValue);

    public static class IOTBoolean extends IOTDataType<Boolean> {
        private boolean iValue = false;

        IOTBoolean(Boolean aValue) {
            super(aValue);
        }

        @Override
        public Boolean getValue() {
            return iValue;
        }

        @Override
        public void setValue(Boolean aValue) {
            iValue = aValue;
        }
    }

    public static class IOTChar extends IOTDataType<Byte> {
        private byte iValue = 0;

        IOTChar() {
        }

        IOTChar(Byte aValue) {
            super(aValue);
        }

        @Override
        public Byte getValue() {
            return iValue;
        }

        @Override
        public void setValue(Byte aValue) {
            iValue = aValue;
        }
    }

    public static class IOTUChar extends IOTChar {
        IOTUChar() {
        }

        IOTUChar(Byte aValue) {
            super(aValue);
        }

        public short getUnsignedValue() {
            return CU8(getValue());
        }

        public void setUnsignedValue(short aValue) {
            setValue(C8(aValue));
        }
    }

    public static class IOTShort extends IOTDataType<Short> {
        private short iValue = 0;

        public IOTShort() {
        }

        public IOTShort(Short aValue) {
            super(aValue);
        }

        @Override
        public Short getValue() {
            return iValue;
        }

        @Override
        public void setValue(Short aValue) {
            iValue = aValue;
        }
    }

    public static class IOTUShort extends IOTShort {
        public IOTUShort() {
        }

        public IOTUShort(Short aValue) {
            super(aValue);
        }

        public int getUnsignedValue() {
            return CU16(getValue());
        }

        public void setUnsignedValue(int aValue) {
            setValue(C16(aValue));
        }
    }

    public static class IOTInt extends IOTDataType<Integer> {
        private int iValue = 0;

        public IOTInt() {
        }

        public IOTInt(Integer aValue) {
            super(aValue);
        }

        @Override
        public Integer getValue() {
            return iValue;
        }

        @Override
        public void setValue(Integer aValue) {
            iValue = aValue;
        }
    }

    public static class IOTUInt extends IOTInt {
        IOTUInt() {
        }

        IOTUInt(Integer aValue) {
            super(aValue);
        }

        public long getUnsignedValue() {
            return CU32(getValue());
        }

        public void setUnsignedValue(long aValue) {
            setValue(C32(aValue));
        }
    }

    public static class IOTLong extends IOTDataType<Long> {
        private long iValue = 0;

        public IOTLong() {
        }

        public IOTLong(Long aValue) {
            super(aValue);
        }

        @Override
        public Long getValue() {
            return iValue;
        }

        @Override
        public void setValue(Long aValue) {
            iValue = aValue;
        }
    }

    public static class IOTULong extends IOTLong {
        public IOTULong() {
        }

        public IOTULong(Long aValue) {
            super(aValue);
        }

        public BigInteger getUnsignedValue() {
            return CU64(getValue());
        }

        public void setUnsignedValue(BigInteger aValue) {
            setValue(C64(aValue));
        }
    }

    public static class IOTString extends IOTDataType<String> {
        private String iValue = null;

        public IOTString() {
        }

        public IOTString(String aValue) {
            super(aValue);
        }

        @Override
        public String getValue() {
            return iValue;
        }

        @Override
        public void setValue(String aValue) {
            iValue = aValue;
        }

        public void setValue(byte aBuffer[], int aPos, int aLength) {
            if (aBuffer != null && aLength > 0 && aBuffer.length >= aPos + aLength) {
                setValue((new String(aBuffer, aPos, aLength)).trim());
            }
        }
    }

    /*
     * Signed to UnSigned conversion
     */
    public static final byte[] C8Array(short aInt16) {
        byte tBytes[] = new byte[2];

        tBytes[0] = (byte) (aInt16 & 0xFF);
        tBytes[1] = (byte) ((aInt16 >> 8) & 0xFF);

        return tBytes;
    }

    public static final byte[] C8Array(int aInt32) {
        byte tBytes[] = new byte[4];

		/*
		0000 0000 0000 0000 0000 0000 0000 0000
		0000 0000 0000 0000 0000 0000 1111 1111
		 */
        tBytes[0] = (byte) (aInt32 & 0xFF);
        tBytes[1] = (byte) ((aInt32 >> 8) & 0xFF);
        tBytes[2] = (byte) ((aInt32 >> 16) & 0xFF);
        tBytes[3] = (byte) ((aInt32 >> 24) & 0xFF);

        return tBytes;
    }

    public static final byte[] C8Array(long aInt64) {
        byte tBytes[] = new byte[8];

        tBytes[0] = (byte) (aInt64 & 0xFF);
        tBytes[1] = (byte) ((aInt64 >> 8) & 0xFF);
        tBytes[2] = (byte) ((aInt64 >> 16) & 0xFF);
        tBytes[3] = (byte) ((aInt64 >> 24) & 0xFF);
        tBytes[4] = (byte) ((aInt64 >> 32) & 0xFF);
        tBytes[5] = (byte) ((aInt64 >> 40) & 0xFF);
        tBytes[6] = (byte) ((aInt64 >> 48) & 0xFF);
        tBytes[7] = (byte) ((aInt64 >> 56) & 0xFF);

        return tBytes;
    }

    public static final byte C8(short aUInt8) {
        if (aUInt8 > Byte.MAX_VALUE) {
            return (byte) (aUInt8 - (short) 0xFF - (short) 1);
        } else {
            return (byte) aUInt8;
        }
    }

    public static final short CU8(byte aInt8) {
        if (aInt8 < 0) {
            return (short) ((short) aInt8 + (short) 0xFF + (short) 1);
        } else {
            return aInt8;
        }
    }

    public static final short C16(int aUInt16) {
        if (aUInt16 > Short.MAX_VALUE) {
            return (short) (aUInt16 - (int) 0xFFFF - (int) 1);
        } else {
            return (short) aUInt16;
        }
    }

    public static final int CU16(short aInt16) {
        if (aInt16 < 0) {
            return (int) ((int) aInt16 + (int) 0xFFFF + (int) 1);
        } else {
            return aInt16;
        }
    }

    public static final int C32(byte aBytes[], int aStartPos) {
        if (aBytes != null && aBytes.length - aStartPos >= 4) {
            return (int) ((int) aBytes[aStartPos + 0] & 0xFF) |
                    (((int) aBytes[aStartPos + 1] & 0xFF) << 8) |
                    (((int) aBytes[aStartPos + 2] & 0xFF)) << 16 |
                    (((int) aBytes[aStartPos + 3] & 0xFF) << 24);
        }

        return 0;
    }

    public static final long C64(byte aBytes[], int aStartPos) {
        if (aBytes != null && aBytes.length - aStartPos >= 8) {
            return (long) ((long) aBytes[aStartPos + 0] & 0xFF) |
                    (((long) aBytes[aStartPos + 1] & 0xFF) << 8) |
                    (((long) aBytes[aStartPos + 2] & 0xFF)) << 16 |
                    (((long) aBytes[aStartPos + 3] & 0xFF)) << 24 |
                    (((long) aBytes[aStartPos + 4] & 0xFF)) << 32 |
                    (((long) aBytes[aStartPos + 5] & 0xFF)) << 40 |
                    (((long) aBytes[aStartPos + 6] & 0xFF)) << 48 |
                    (((long) aBytes[aStartPos + 7] & 0xFF) << 56);
        }

        return 0;
    }

    public static final int C32(long aUInt32) {
        if (aUInt32 > Integer.MAX_VALUE) {
            return (int) (aUInt32 - (long) 0xFFFFFFFF - (long) 1);
        } else {
            return (int) aUInt32;
        }
    }

    public static final long CU32(int aInt32) {
        if (aInt32 < 0) {
            return (long) ((long) aInt32 + (long) 0xFFFFFFFF + (long) 1);
        } else {
            return aInt32;
        }
    }

    public static final long C64(BigInteger aUInt64) {
        if (aUInt64 != null) {
            return aUInt64.longValue();
        }

        return 0;
    }

    public static final BigInteger CU64(long aUInt64) {
        return new BigInteger(Long.toHexString(aUInt64), 16);
    }

    //==========modify start==============
    public static final short C16(byte[] aByte, int pos) {
        if (aByte.length >= pos + 2) {
            return (short) ((short) (((short) aByte[pos + 1] & 0xff) << 8) | ((short) aByte[pos] & 0xff));
        }
        return 0;
    }
    //==========modify end==============
}
